import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class SvMon extends UnicastRemoteObject implements ISvMon {

	/**
     * 
     */
    private static final long serialVersionUID = -7883270519966359466L;
    
    private FileUtils fileUtils;
    
    private static final Pattern DISTRIBUTION = Pattern.compile("DISTRIB_DESCRIPTION=\"(.*)\"", Pattern.MULTILINE);
    private static final Pattern UPTIME_PATTERN = Pattern.compile("([\\d]*).*");
    private static final Pattern TOTAL_MEMORY_PATTERN = Pattern.compile("MemTotal:\\s+(\\d+) kB", Pattern.MULTILINE);
    private static final Pattern FREE_MEMORY_PATTERN = Pattern.compile("MemFree:\\s+(\\d+) kB", Pattern.MULTILINE);
    private static final Pattern TOTAL_SWAP_PATTERN = Pattern.compile("SwapTotal:\\s+(\\d+) kB", Pattern.MULTILINE);
    private static final Pattern FREE_SWAP_PATTERN = Pattern.compile("SwapFree:\\s+(\\d+) kB", Pattern.MULTILINE);
    private static final Pattern NUM_CPU_PATTERN =
        Pattern.compile("processor\\s+:\\s+(\\d+)", Pattern.MULTILINE);
    private static final Pattern CPU_CUR_FREQ_PATTERN =
        Pattern.compile("cpu MHz\\s+:\\s+(\\d+)", Pattern.MULTILINE);
    private static final Pattern CPU_MODEL_PATTERN =
        Pattern.compile("model name[^@]*@\\s+([0-9.A-Za-z]*)", Pattern.MULTILINE);
    
	protected SvMon() throws RemoteException {
	    super();
	    fileUtils = new FileUtils();
    }

	private String readFromCommand(String command) {
		String output = "";
		
		String lines = "";
		
		try {

			Process p = Runtime.getRuntime().exec(command);
	            
			BufferedReader stdInput = new BufferedReader(new 
	                 InputStreamReader(p.getInputStream()));

			while ((lines = stdInput.readLine()) != null) {
				output += lines;
			}
		}
	        catch (IOException e) {
	            e.printStackTrace();
	        }
		
		return output;
	}
	
	@Override
	public String osName() throws RemoteException {
		String distribution = fileUtils.runRegexOnFile(DISTRIBUTION, "/etc/lsb-release");
        if (null == distribution) {
            return System.getProperty("os.name");
        }
        return distribution;
	}

	@Override
	public long uptimeInSeconds() throws RemoteException {
		String uptime = fileUtils.runRegexOnFile(UPTIME_PATTERN, "/proc/uptime");
        return Long.parseLong(uptime);
	}

	@Override
	public MemoryStats physical() throws RemoteException {
		String totalMemory = fileUtils.runRegexOnFile(TOTAL_MEMORY_PATTERN, "/proc/meminfo");
        long total = Long.parseLong(totalMemory) * 1024;
        String freeMemory = fileUtils.runRegexOnFile(FREE_MEMORY_PATTERN, "/proc/meminfo");
        long free = Long.parseLong(freeMemory) * 1024;
        return new MemoryStats(free, total);
	}

	@Override
	public MemoryStats swap() throws RemoteException {
		String totalMemory = fileUtils.runRegexOnFile(TOTAL_SWAP_PATTERN, "/proc/meminfo");
        long total = Long.parseLong(totalMemory) * 1024;
        String freeMemory = fileUtils.runRegexOnFile(FREE_SWAP_PATTERN, "/proc/meminfo");
        long free = Long.parseLong(freeMemory) * 1024;
        return new MemoryStats(free, total);
	}

	
	public String kernelVersion() throws RemoteException {
		return readFromCommand("uname -r");
	}

	public String cpuModel() throws RemoteException {
		String model = "";
		
		model = fileUtils.runRegexOnFile(CPU_MODEL_PATTERN, "/proc/cpuinfo");
		
		return model;
	}

	public float cpuFrequency() throws RemoteException {
		float cpuFr = 0.0f;
		
		FileInputStream fstream;
		DataInputStream in;
		BufferedReader br;
		
		try {
			fstream = new FileInputStream("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));
			
			cpuFr = Float.parseFloat(br.readLine());
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			cpuFr = 0.0f;
			e.printStackTrace();
		}
		
		return cpuFr;
		
	}

	public float[] cpuCoreFrequency() throws RemoteException {
		int n = cpuNum();
		
		float[] freqs = new float[n];
		
		String command = "sed -n 's/\\(cpu MHz.*\\) \\([0-9]*\\.[0-9]*\\)/\\2/p' /proc/cpuinfo 1> /tmp/output.trs";
		
		String lines = "";
		
		FileInputStream fstream;
		DataInputStream in;
		BufferedReader br;
		
		try {

			Process p = Runtime.getRuntime().exec(command);
	            
			fstream = new FileInputStream("/tmp/output.trs");
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));

			int i = 0;
			while ((lines = br.readLine()) != null) {
				//System.out.println("i = " + i + " :: " + lines);
				freqs[i] = Float.parseFloat(lines);
				i++;
			}
		}
	        catch (IOException e) {
	            e.printStackTrace();
	        }
		catch (NumberFormatException e) {
			e.printStackTrace();
		}
		
	return freqs;
	}

	public int cpuNum() throws RemoteException {
        int numCpus = 0;
        
        try {
            String cpuInfo = fileUtils.slurp("/proc/cpuinfo");
            Matcher matcher = NUM_CPU_PATTERN.matcher(cpuInfo);
            while (matcher.find()) {
                numCpus++;
            }
            return numCpus;
        } catch (IOException ioe) {
        	return 0;
        }
	}

	public float[] cpuLoad() throws RemoteException {
		float[] loads = new float[3];
		
		String[] splitted;
		
		FileInputStream fstream;
		DataInputStream in;
		BufferedReader br;
		
		try {
			fstream = new FileInputStream("/proc/loadavg");
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));
			
			splitted = br.readLine().split(" ");
			
			loads[0] = Float.parseFloat(splitted[0]);
			loads[1] = Float.parseFloat(splitted[1]);
			loads[2] = Float.parseFloat(splitted[2]);
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return loads;
	}

}


